'use strict'

process.chdir(__dirname)

const fs = require('fs')

/**
 * 初始化配置文件，如果没有进行配置则自动初始化。
 */
 try {
  fs.accessSync('./config')
} catch (err) {
  fs.mkdirSync('./config')
}

try {
  fs.accessSync('./config/config.js')
} catch (err) {
  let flist = fs.readdirSync('./config-example', {withFileTypes: true})
  for (let f of flist) {
    if (!f.isFile()) continue
    fs.copyFileSync(`./config-example/${f.name}`, `./config/${f.name}`)
  }
  console.log('首次运行服务，已经初始化配置文件，请在config/目录中进行配置文件的设置后再次运行服务。')
  process.exit(1)
}

const titbit = require('titbit')
const tbloader = require('titbit-loader')
const {resource, mixlogger, jwt} = require('titbit-toolkit')
const {initORM} = require('psqlorm')
const config = require('./config/config.js')
const funcs = require('./lib/funcs.js')
const cdpc = require('cdpc')
const npargv = require('npargv')
const httpc = require('titbit-httpc')
const titbitToken = require('titbit-token')
const sm4password = require('sm4-password')

const {args} = npargv({
  '@autoDefault': true,

  '--test' : {
    name: 'test',
    default: false,
  },

  '--debug' : {
    name: 'debug',
    default: false,
  },
})

let loadfile = '/tmp/nodeapp-load.log'

if (process.platform.indexOf('win') === 0) {
  loadfile = __dirname + '/tmp/nodeapp-load.log'
}

if (config.httpCompatible) {
  config.http2 = false
  config.https = true
}

const app = new titbit({
  debug: args.debug,
  maxUrlLength: 4096,
  maxQuery: 50,
  monitorTimeSlice: 172,
  //loadInfoFile: loadfile,
  loadInfoFile: '--mem',
  loadInfoType: 'json',
  https: config.https,
  http2: config.http2,
  cert: (config.https || config.http2 )? config.cert : '',
  key: (config.https || config.http2 )? config.key : '',
  server: {
    handshakeTimeout: 10000
  },
  maxLoadRate: 62,

  //启用全局日志
  globalLog: true,

  logType: 'file',
  logFile: './tmp/success.log',
  errorLogFile: './tmp/error.log',
  
  //日志文件最大条数
  logMaxLines: 10000,

  //最大历史日志文件数量
  logHistory: 50,

  errorHandle: (err, tname) => {
    if (tname !== '--ERR-REQUEST--') {
      console.error(tname, err)
    }
  }
})

if (config.httpCompatible) {
  ;(new httpc).init(app)
}

if (app.isMaster) {
  ;(new mixlogger({
    logHandle: (w, msg, handle) => {
      if (msg.log.indexOf('/sys/loadinfo', 7) > 0) {
        return false
      }
    
      return true
    }
  })).init(app)

  try {
    fs.accessSync(__dirname + '/tmp')
  } catch (err) {
    fs.mkdirSync(__dirname + '/tmp')
  }

  [
    '/storage', 
    '/storage/images', 
    '/storage/audios', 
    '/storage/videos', 
    '/storage/files'
  ].forEach(a => {
    funcs.tryMkdir(__dirname + a).catch(err => {
      console.error(err)
    })
  })


  ;((async () => {
    if (!config.dbserver) return;
    console.log('正在初始化数据库服务···')
    /**
     * cdpc是基于child_process开发的子进程管理扩展，主要为了在Node.js中让复杂的进程管理变得简单而设计。
     * 它可以运行JS文件以及系统上的任何其他命令，比如 shell脚本、编译好的程序等。
     * {@link https://gitee.com/daoio/cdpc}
     */
    const cm = new cdpc()

    await new Promise((rv, rj) => {
      //启动数据库服务。
      cm.runChilds([{
          name: 'postgres',
          command: `..\\${config.pgsqlVersion}\\bin\\postgres.exe`,
          cwd: __dirname + '\\..\\',
          args: [
            '-D', `${__dirname}\\..\\${config.pgsqlVersion}-data`, '-p', config.dbconfig.port || 5432
          ],
          //自动重启模式为always
          restart: 'always',
          //重启延迟为1000毫秒
          restartDelay: 1000,
          onError: err => {
            console.error(err)
            rj(err)
          },
          /**
           * @param {object} ch child_process模块。
           * @param {object} cm 就是new cdpc后的返回值。
           */
          callback: (ch, cm) => {
            ch.on('spawn', code => {
              rv(code)
            })
          }
  
        }])
    })

    return cm
  
  })()).then(async (cm) => {
    if (!config.dbserver) return;
    //数据库进程运行后，需要等待数据库初始化。
    await new Promise((rv) => { setTimeout(() => {rv()}, 600) })
  
    //运行自动同步数据库表结构程序。
    cm.add({
      name: 'init-db',
      file: 'dbsync.js',
      //失败后自动重启
      restart: 'fail-count',
      //限制仅重启1次
      restartLimit: 1,
      //运行完成后自动移除子进程配置项。
      autoRemove: true,
      options: {
        stdio: ['ignore', 1, 2]
      },
      callback: (ch, cm) => {
        ch.on('spawn', () => {
          console.log('同步数据表结构···')
        })
      }
    });
  
  })
  
}

if (config.autoWorker > 0 && config.autoWorker > config.worker) {
  app.autoWorker(config.autoWorker)
}

if (app.isWorker) {
  let pqorm = initORM(config.dbconfig)
  pqorm.setSchema(config.dbschema)
  app.addService('loadfile', loadfile)
  app.addService('DEBUG', args.debug)
  app.addService('TEST', args.test)

  app.addService('appDir', __dirname)
  app.addService('dataPath', `${__dirname}/data`)
  app.addService('storagePath', `${__dirname}/storage`)

  app.addService('pqorm', pqorm)
  app.addService('config', config)
  app.addService('funcs', funcs)

  let tbl = new tbloader({
    mdb: pqorm
  })
  
  tbl.init(app)

  let rse = new resource({
    staticPath: `${__dirname}/public/static`,
    routePath : '/static/*',
    routeGroup: '_static',
    decodePath: true,
  })
  
  rse.init(app)

  app.get('/favicon.ico', async c => {}, {group: '_static'})
  
  app.get('/', async c => {
    await c.helper.pipe('./public/index.html', c.reply)
  })

  app.get('/page/:name', async c => {
    try {
      await c.helper.pipe( `./public/${c.param.name}.html`, c.reply)
    } catch (err) {
      c.service.DEBUG && console.error(err)
      c.status(404)
      await c.helper.pipe( `./public/errorpages/404.html`, c.reply)
    }
  })

  //初始化token验证实例并挂载到service

  //用于amdin分组的token验证
  let atok = new titbitToken({
    iv: config.admin.iv || '1qaz2wsx3edc4rfv',
    key: config.admin.key,
    expires: config.admin.expires > 0 ? config.admin.expires : 3600000
  })

  app.addService('atok', atok)

  //用于api分组的token验证
  let tok = new titbitToken({
    iv: config.api.iv || '4rfv5tgb6yhn7ujm',
    key: config.api.key,
    expires: config.api.expires > 0 ? config.api.expires : 3600000
  })

  app.addService('tok', tok)

  app.addService('upass', new sm4password())

}

//设定负载消息传递
if (app.isWorker) {
  app.workerMsg(msg => {
    if (msg.type === 'loadinfo') {
      global.__loadinfo__ = msg.data
    }
  })
} else {
  app.setMsgEvent('loadinfo', (wk, msg) => {
    wk.send({
      type: 'loadinfo',
      data: app.monitor.loadCache
    })
  })
}

let port = 1234

if (app.isMaster) {
  app.daemon(config.port || port, config.host || '::', config.worker)
} else {
  app.send('loadinfo', {})
  app.addService('loadinfo', () => {
    app.send('loadinfo', {})
  })
  setTimeout(() => {
    app.daemon(config.port || port, config.host || '::', config.worker)
  }, 685)
}
